package store.lunangangster.nodeflow.builder;

import static store.lunangangster.nodeflow.enums.FlowType.FLOW_ELSE;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_FOR;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_IF;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_SWITCH;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_THEN;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_WHEN;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_WHILE;
import static store.lunangangster.nodeflow.enums.NodeType.NODE_FOR;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import store.lunangangster.nodeflow.common.BaseConstant;
import store.lunangangster.nodeflow.core.component.ExitComponent;
import store.lunangangster.nodeflow.core.component.ForComponent;
import store.lunangangster.nodeflow.core.component.IfComponent;
import store.lunangangster.nodeflow.core.component.WhileComponent;
import store.lunangangster.nodeflow.enums.FlowType;
import store.lunangangster.nodeflow.exception.ExecutableItemNotFoundException;
import store.lunangangster.nodeflow.exception.RuleSourceParseException;
import store.lunangangster.nodeflow.parser.base.expression.ExpressionParserHelper;
import store.lunangangster.nodeflow.rt.FlowBus;
import store.lunangangster.nodeflow.rt.element.Executable;
import store.lunangangster.nodeflow.rt.element.Node;
import store.lunangangster.nodeflow.rt.element.flow.Flow;
import store.lunangangster.nodeflow.rt.element.flow.ForFlow;
import store.lunangangster.nodeflow.rt.element.flow.IfFlow;
import store.lunangangster.nodeflow.rt.element.flow.SwitchFlow;
import store.lunangangster.nodeflow.rt.element.flow.ThenFlow;
import store.lunangangster.nodeflow.rt.element.flow.WhenFlow;
import store.lunangangster.nodeflow.rt.element.flow.WhileFlow;

/**
 * Flow的构建器
 *
 * @author cwk
 * @version 1.0
 */
public class FlowBuilder {

	private static class Snapshot {
		Flow emptyFlow;
		List<Executable> executableList;
		Map<String, String> attributeMap;
		boolean isExpression;
		boolean isSelfClosure;

		public Snapshot(Flow emptyFlow, List<Executable> executableList,
				Map<String, String> attributeMap, boolean isExpression, boolean isSelfClosure) {
			this.emptyFlow = emptyFlow;
			this.executableList = executableList;
			this.attributeMap = attributeMap;
			this.isExpression = isExpression;
			this.isSelfClosure = isSelfClosure;
		}
	}

	private static final Map<FlowType, Function<Snapshot, Flow>> flowMaker = new HashMap<FlowType, Function<Snapshot, Flow>>() {
		{
			put(FLOW_THEN, FlowBuilder::buildThenFlow);
			put(FLOW_WHEN, FlowBuilder::buildWhenFlow);
			put(FLOW_IF, FlowBuilder::buildIfFlow);
			put(FLOW_ELSE, FlowBuilder::buildThenFlow);
			put(FLOW_FOR, FlowBuilder::buildForFlow);
			put(FLOW_WHILE, FlowBuilder::buildWhileFlow);
			put(FLOW_SWITCH, FlowBuilder::buildSwitchFlow);
		}
	};

	private static final Map<FlowType, Function<Snapshot, Flow>> elFlowMaker = new HashMap<FlowType, Function<Snapshot, Flow>>() {
		{
			put(FLOW_THEN, FlowBuilder::buildELThenFlow);
			put(FLOW_WHEN, FlowBuilder::buildELWhenFlow);
			put(FLOW_IF, FlowBuilder::buildELIFFlow);
			put(FLOW_FOR, FlowBuilder::buildELForFLow);
			put(FLOW_WHILE, FlowBuilder::buildELWhileFlow);
			put(FLOW_SWITCH, FlowBuilder::buildELSwitchFlow);
		}
	};

	/**
	 * 通过Base解析器解析规则来构造Flow。使用{@link FlowType#newFlowInstance()}获取相应Flow的实例对象，将executableList和标签的合法属性列表传入
	 * @param flowType        Flow类型
	 * @param executableList  执行子列表
	 * @param attributeMap    属性列表
	 * @param isExpression		是否是Expression式的Flow
	 * @param isSelfClosure   是否是单闭合标签
	 * @return 构建出的Flow对象
	 */
	public static Flow buildFlow(FlowType flowType, List<Executable> executableList, Map<String, String> attributeMap, boolean isExpression, boolean isSelfClosure) {
		if (flowType == null) {
			return Flow.EMPTY_FLOW;
		}
		// 根据类型创建实例对象
		Flow emptyFlow = flowType.newFlowInstance();
		return flowMaker.get(flowType).apply(new Snapshot(emptyFlow, executableList, attributeMap, isExpression, isSelfClosure));
	}

	/**
	 * 通过EL解析器解析规则来构造Flow。该过程不需要使用attributeMap、isExpression、isSelfClosure属性
	 * @param flowType				Flow类型
	 * @param executableList	执行子列表
	 * @return 构建出的Flow对象
	 */
	public static Flow buildELFLow(FlowType flowType, List<Executable> executableList) {
		if (flowType == null) {
			return Flow.EMPTY_FLOW;
		}
		// 根据类型创建实例对象
		Flow emptyFlow = flowType.newFlowInstance();
		return elFlowMaker.get(flowType).apply(new Snapshot(emptyFlow, executableList, null, false, false));
	}

	private static Flow buildThenFlow(Snapshot snapshot) {
		ThenFlow flow = (ThenFlow) snapshot.emptyFlow;
		flow.setExecutableList(snapshot.executableList);
		return flow;
	}

	private static Flow buildWhenFlow(Snapshot snapshot) {
		WhenFlow flow = (WhenFlow) snapshot.emptyFlow;
		flow.setExecutableList(snapshot.executableList);
		if (snapshot.attributeMap != null && snapshot.attributeMap.get(BaseConstant.ANY) != null) {
			flow.setAny(Boolean.parseBoolean(snapshot.attributeMap.get(BaseConstant.ANY)));
		}
		return flow;
	}

	private static Flow buildIfFlow(Snapshot snapshot) {
		IfFlow ifFlow = (IfFlow) snapshot.emptyFlow;
		if (CollUtil.isEmpty(snapshot.executableList)) {
			return ifFlow;
		}
		if (snapshot.isExpression) {
			// 如果是Expression式的IfFlow：<then value="a(b|c)"/>，则executableList中装载了IfNode、TrueBranch、FalseBranch
			// Expression式在解析阶段就会保证"a"是一个IfNodeComponent
			ifFlow.setExecutableList(ListUtil.toList(snapshot.executableList.get(0)));
			ifFlow.setBranch(
					snapshot.executableList.size() >= 2 ? snapshot.executableList.get(1) : null,
					snapshot.executableList.size() >= 3 ? snapshot.executableList.get(2) : null);
		} else {
			// 标签式的IfFlow，value一定代表IfNode
			String value = snapshot.attributeMap.get(BaseConstant.VALUE);
			Executable ifExecutable = snapshot.executableList.get(0);
			if (!(ifExecutable instanceof Node) || !(((Node) ifExecutable).getNodeComponent() instanceof IfComponent)) {
				throw new RuleSourceParseException(String.format("Incompatible executable for IfFlow: expression[\"%s\"] is not a IfNodeComponent", value));
			}
			ifFlow.setExecutableList(CollUtil.toList(ifExecutable));
			if (snapshot.isSelfClosure) {
				// 如果是单标签式的IfFlow，那么需要解析"true"和"false"属性：<if value="a" true="b" false="c"/>，value部分只能是一个简单的IfNode
				if (snapshot.executableList.size() != 1) {
					throw new RuleSourceParseException(String.format("Incompatible executable for IfFlow: expression[\"%s\"] contains one more node", value));
				}
				if (snapshot.attributeMap == null) {
					snapshot.attributeMap = Collections.emptyMap();
				}
				// True or False branch is allowed to be null
				List<Executable> trueExecutable = ExpressionParserHelper.resolveValue(
						snapshot.attributeMap.get(BaseConstant.TRUE));
				List<Executable> falseExecutable = ExpressionParserHelper.resolveValue(
						snapshot.attributeMap.get(BaseConstant.FALSE));
				// trueBranch和falseBranch可以为null，会在后续合并封装
				ifFlow.setBranch(
						FlowBuilder.buildFlow(FLOW_THEN, trueExecutable, null, false, true),
						FlowBuilder.buildFlow(FLOW_THEN, falseExecutable, null, false, true)
				);
			} else {
				// 如果是双标签式的IfFlow，则executableList是trueBranch，value字段为IfNode
				// falseBranch会在后续合并封装
				ifFlow.setTrueBranch(FlowBuilder.buildFlow(FLOW_THEN, snapshot.executableList, null, false, false));
			}
		}
		return ifFlow;
	}

	private static Flow buildForFlow(Snapshot snapshot) {
		ForFlow forFlow = (ForFlow) snapshot.emptyFlow;
		Node loopNode;
		List<Executable> executableList;
		Node exitNode = null;
		if (CollUtil.isEmpty(snapshot.executableList)) {
			throw new RuleSourceParseException("Incompatible executable for ForFlow: for tag has no ForNode");
		}
		if (snapshot.isExpression) {
			// 如果是Expression式的ForFlow，则executableList[0]是ForNode，其余是ForBody
			loopNode = (Node) snapshot.executableList.get(0);
			executableList = new ArrayList<>(snapshot.executableList.size());
			for (int i = 1; i < snapshot.executableList.size(); i++) {
				executableList.add(snapshot.executableList.get(i));
			}
		} else {
			if (snapshot.isSelfClosure) {
				// 如果是单标签式的ForFlow，则executableList[0]是ForNode，"body"属性是ForBody，"exit"属性是ExitNode
				if (CollUtil.isEmpty(snapshot.executableList)) {
					throw new RuleSourceParseException("Incompatible executable for ForFlow: for tag has no ForNode");
				}
				loopNode = (Node) snapshot.executableList.get(0);
				executableList = null;
				if (!CollUtil.isEmpty(snapshot.attributeMap)) {
					String body = snapshot.attributeMap.get(BaseConstant.BODY);
					executableList = ExpressionParserHelper.resolveValue(body);
				}
			} else {
				// 如果是双标签式的ForFlow，则"value"属性是ForNode，executableList是ForBody，"exit"属性是ExitNode
				if (snapshot.attributeMap == null) {
					throw new RuleSourceParseException("Incompatible executable for ForFlow: for tag has no ForNode");
				}
				String value = snapshot.attributeMap.get(BaseConstant.VALUE);
				Executable executable;
				try {
					// 如果从FlowBus中找不到注册的Node，则尝试使用数字进行解析
					executable = ExpressionParserHelper.resolveCommonExpression(value);
				} catch (ExecutableItemNotFoundException e) {
					int loopTimes;
					try {
						loopTimes = Integer.parseInt(value);
					} catch (NumberFormatException nfe) {
						throw new RuleSourceParseException("Incompatible executable for ForFlow: for tag has no ForNode", e);
					}
					// 创建一个临时的LoopComponent
					ForComponent fixedLoopComp = ForComponent.getFixedLoopComponent(loopTimes);
					// 在解析Expression时生成Node的实例，这个Node不会被添加到FlowBus中，方便GC
					executable = NodeBuilder.start()
							.id(null)
							.component(fixedLoopComp)
							.clazz(ForComponent.class)
							.type(NODE_FOR)
							.build(false);
				}
				if (executable instanceof Node && ((Node) executable).getNodeComponent() instanceof ForComponent) {
					loopNode = (Node) executable;
				} else {
					throw new RuleSourceParseException("Incompatible executable for ForFlow: forNode is not a ForComponent");
				}
				executableList = snapshot.executableList;
			}

			// 处理ExitNode
			String exit = snapshot.attributeMap.get(BaseConstant.EXIT);
			if (!CollUtil.isEmpty(snapshot.attributeMap) && !StrUtil.isBlank(exit)) {
				try {
					Executable exitExecutable = ExpressionParserHelper.resolveCommonExpression(exit);
					if (exitExecutable instanceof Node && ((Node) exitExecutable).getNodeComponent() instanceof ExitComponent) {
						exitNode = (Node) exitExecutable;
					}
				} catch (ExecutableItemNotFoundException e) {
					throw new RuleSourceParseException("Incompatible executable for ForFlow: for tag[" + exit + "] suppose to be a ExitNode", e);
				}
			}
		}
		forFlow.setLoopNode(loopNode);
		forFlow.setExecutableList(executableList);
		forFlow.setExitNode(exitNode);
		return forFlow;
	}

	public static Flow buildWhileFlow(Snapshot snapshot) {
		WhileFlow whileFlow = (WhileFlow) snapshot.emptyFlow;
		Node whileNode;
		List<Executable> whileBody;
		Node exitNode = null;
		if (CollUtil.isEmpty(snapshot.executableList)) {
			throw new RuleSourceParseException("Incompatible executable for WhileFlow: while tag has no WhileNode");
		}
		if (snapshot.isExpression) {
			// 如果是Expression式的WhileFLow，则executableList[0]是WhileNode、其余为WhileBody
			whileNode = (Node) snapshot.executableList.get(0);
			if (snapshot.executableList.size() == 1) {
				whileBody = null;
			} else {
				whileBody = new ArrayList<>();
				for (int i = 1; i < snapshot.executableList.size(); i++) {
					whileBody.add(snapshot.executableList.get(i));
				}
			}
		} else {
			if (snapshot.isSelfClosure) {
				// 如果是单标签式的WhileFlow，则executableList[0]是WhileNode、"body"属性是WhileBody
				whileNode = (Node) snapshot.executableList.get(0);
				if (!CollUtil.isEmpty(snapshot.attributeMap)) {
					String body = snapshot.attributeMap.get(BaseConstant.BODY);
					whileBody = ExpressionParserHelper.resolveValue(body);
				} else {
					whileBody = null;
				}
			} else {
				// 如果是双标签式的WhileFlow，则"value"属性是WhileNode、executableList是WhileBody
				if (CollUtil.isEmpty(snapshot.attributeMap)) {
					throw new RuleSourceParseException("Incompatible executable for WhileFLow: while tag has no WhileNode");
				}
				String value = snapshot.attributeMap.get(BaseConstant.VALUE);
				Executable executable;
				try {
					executable = ExpressionParserHelper.resolveCommonExpression(value);
				} catch (ExecutableItemNotFoundException e) {
					throw new RuleSourceParseException("Incompatible executable for WhileFLow: WhileNode is not a WhileComponent");
				}
				if (executable instanceof Node && ((Node) executable).getNodeComponent() instanceof WhileComponent) {
					whileNode = (Node) executable;
					whileBody = snapshot.executableList;
				} else {
					throw new RuleSourceParseException("Incompatible executable for WhileFLow: WhileNode is not a WhileComponent");
				}
			}
			// 处理ExitNode
			String exit = snapshot.attributeMap.get(BaseConstant.EXIT);
			if (!CollUtil.isEmpty(snapshot.attributeMap) && !StrUtil.isBlank(exit)) {
				try {
					Executable exitExecutable = ExpressionParserHelper.resolveCommonExpression(exit);
					if (exitExecutable instanceof Node && ((Node) exitExecutable).getNodeComponent() instanceof ExitComponent) {
						exitNode = (Node) exitExecutable;
					}
				} catch (ExecutableItemNotFoundException e) {
					throw new RuleSourceParseException("Incompatible executable for WhileFlow: for tag[" + exit + "] suppose to be a ExitNode", e);
				}
			}
		}
		whileFlow.setWhileNode(whileNode);
		whileFlow.setExecutableList(whileBody);
		whileFlow.setExitNode(exitNode);
		return whileFlow;
	}

	private static Flow buildSwitchFlow(Snapshot snapshot) {
		SwitchFlow switchFlow = (SwitchFlow) snapshot.emptyFlow;
		Node switchNode;
		List<Executable> switchList;
		if (CollUtil.isEmpty(snapshot.executableList)) {
			throw new RuleSourceParseException("Incompatible executable for SwitchFlow: SwitchList should not be empty");
		}
		switchNode = (Node) snapshot.executableList.get(0);
		if (snapshot.isExpression) {
			// 如果是Expression式的SwitchFLow，则executableList[0]是SwitchNode、其余为SwitchList
			switchList = new ArrayList<>(snapshot.executableList.size());
			for (int i = 1; i < snapshot.executableList.size(); i++) {
				switchList.add(snapshot.executableList.get(i));
			}
		} else {
			if (snapshot.isSelfClosure) {
				// 如果是单标签式的SwitchFLow，则executableList[0]是SwitchNode、"body"属性是SwitchList
				if (CollUtil.isEmpty(snapshot.attributeMap) || StrUtil.isBlank(snapshot.attributeMap.get(BaseConstant.BODY))) {
					throw new RuleSourceParseException("Incompatible executable for SwitchFlow: SwitchList should not be empty");
				}
				String body = snapshot.attributeMap.get(BaseConstant.BODY);
				String[] sunSplit = body.split("\\|");
				switchList = new ArrayList<>(sunSplit.length);
				for (String subExpression : sunSplit) {
					Executable subExecutable = FlowBus.getExecutable(subExpression.trim());
					if (subExecutable == null) {
						throw new RuleSourceParseException("Incompatible executable for SwitchFlow: element in switchList should all be common Node or Chain");
					}
					switchList.add(subExecutable);
				}
			} else {
				// 不支持多标签
				throw new RuleSourceParseException("Incompatible executable for SwitchFlow: switch tag not support doubleClosure tag");
			}
		}
		switchFlow.setSwitchNode(switchNode);
		switchFlow.setExecutableList(switchList);
		return switchFlow;
	}


	private static Flow buildELThenFlow(Snapshot snapshot) {
		return buildThenFlow(snapshot);
	}

	private static Flow buildELWhenFlow(Snapshot snapshot) {
		WhenFlow flow = (WhenFlow) snapshot.emptyFlow;
		flow.setExecutableList(snapshot.executableList);
		return flow;
	}

	private static Flow buildELForFLow(Snapshot snapshot) {
		ForFlow forFlow = (ForFlow) snapshot.emptyFlow;
		forFlow.setLoopNode((Node) snapshot.executableList.get(0));
		return forFlow;
	}

	private static Flow buildELIFFlow(Snapshot snapshot) {
		IfFlow ifFlow = (IfFlow) snapshot.emptyFlow;
		ifFlow.setExecutableList(snapshot.executableList);
		return ifFlow;
	}

	private static Flow buildELWhileFlow(Snapshot snapshot) {
		WhileFlow whileFlow = (WhileFlow) snapshot.emptyFlow;
		whileFlow.setWhileNode((Node) snapshot.executableList.get(0));
		return whileFlow;
	}

	private static Flow buildELSwitchFlow(Snapshot snapshot) {
		SwitchFlow switchFlow = (SwitchFlow) snapshot.emptyFlow;
		switchFlow.setSwitchNode((Node) snapshot.executableList.get(0));
		return switchFlow;
	}
}
